#include "FileStream.h"
#include <Rz/String.h>

#include <iostream>

namespace Rz { namespace IO {

FileStream::FileStream()
	: _stream(NULL)
{

}

FileStream::FileStream(const String& filename)
{
	this->Open(filename);
}

FileStream::FileStream(const String& filename, FileMode mode)
{
	this->Open(filename, mode);
}

FileStream::FileStream(const String& filename, FileMode mode, FileAccess access)
{
	this->Open(filename, mode, access);
}

FileStream::~FileStream()
{
	this->Close();
}

void FileStream::Open(const String& filename)
{
	this->Open(filename, FileMode::OpenOrCreate);
}

void FileStream::Open(const String& filename, FileMode mode)
{
	this->Open(filename, mode, FileAccess::ReadWrite);
}

void FileStream::Open(const String& filename, FileMode mode, FileAccess access)
{
	const char* filenameChar = filename.GetCharArray();

	_filename = filename;
	_fileMode = mode;
	_fileAccess = access;

	switch (access)
	{
		case FileAccess::Read:
			switch (mode)
			{
				case FileMode::Append:
					_stream = fopen(filenameChar, "ab");
					break;

				case FileMode::Create:
					_stream = fopen(filenameChar, "r+b");
					break;

				case FileMode::CreateNew:
					_stream = fopen(filenameChar, "r+b");
					break;

				case FileMode::Open:
					_stream = fopen(filenameChar, "rb");
					break;

				case FileMode::OpenOrCreate:
					_stream = fopen(filenameChar, "r+b");
					break;

				case FileMode::Truncate:
					_stream = fopen(filenameChar, "r+b");
					break;
			}
			break;

		case FileAccess::Write:
			switch (mode)
			{
				case FileMode::Append:
					_stream = fopen(filenameChar, "ab");
					break;

				case FileMode::Create:
					_stream = fopen(filenameChar, "w+b");
					break;

				case FileMode::CreateNew:
					_stream = fopen(filenameChar, "w+b");
					break;

				case FileMode::Open:
					_stream = fopen(filenameChar, "wb");
					break;

				case FileMode::OpenOrCreate:
					_stream = fopen(filenameChar, "w+b");
					break;

				case FileMode::Truncate:
					_stream = fopen(filenameChar, "w+b");
					break;
			}
			break;

		case FileAccess::ReadWrite:
			switch (mode)
			{
				case FileMode::Append:
					_stream = fopen(filenameChar, "ab+");
					break;

				case FileMode::Create:
					_stream = fopen(filenameChar, "ab+");
					break;

				case FileMode::CreateNew:
					_stream = fopen(filenameChar, "ab+");
					break;

				case FileMode::Open:
					_stream = fopen(filenameChar, "ab+");
					break;

				case FileMode::OpenOrCreate:
					_stream = fopen(filenameChar, "ab+");
					break;

				case FileMode::Truncate:
					_stream = fopen(filenameChar, "ab+");
					break;
			}
			break;
	}	
}

bool FileStream::IsEndOfFile() const
{
	return !!feof(_stream);
}


size_t FileStream::Read(byte_t* buffer, size_t size) const
{
	if (this->CanRead())
	{
		return static_cast<size_t>(fread(static_cast<void*>(buffer), 1, size, _stream));
	}

	return 0;
}

String FileStream::ReadLine() const
{
	if (this->CanRead())
	{
		String result;

		byte_t c;
		while (!this->IsEndOfFile() && (c = this->ReadByte()) != '\n')
		{
			result.Append(String(c));
		}

		return result;
	}

	return "";
}

void FileStream::Write(const byte_t* buffer, size_t size)
{
	if (this->CanWrite())
	{
		fwrite(static_cast<const void*>(buffer), 1, size, _stream);
	}
}

void FileStream::WriteLine(const String& str)
{
	if (this->CanWrite())
	{
		this->Write(reinterpret_cast<const byte_t*>(str.GetCharArray()), str.GetLength());
		this->WriteByte('\r');
		this->WriteByte('\n');
	}
}

void FileStream::Seek(size_t position)
{
	fseek(_stream, static_cast<long>(position), SEEK_SET);
}

size_t FileStream::GetPosition() const
{
	return static_cast<size_t>(ftell(_stream));
}

size_t FileStream::GetLength() const
{
	long tmp = ftell(_stream);
	long result;
	fseek(_stream, 0, SEEK_END);
	result = ftell(_stream);
	fseek(_stream, tmp, SEEK_SET);
	return result;
}

bool FileStream::IsOpened() const
{
	return (_stream != nullptr);
}

bool FileStream::CanSeek() const
{
	return true;
}

bool FileStream::CanRead() const
{
	return ( (_stream != nullptr) && ((_fileAccess == FileAccess::Read) || (_fileAccess == FileAccess::ReadWrite)) );
}

bool FileStream::CanWrite() const
{
	return ( (_stream != nullptr) && ((_fileAccess == FileAccess::Write) || (_fileAccess == FileAccess::ReadWrite)) );
}


void FileStream::Close()
{
	if (_stream != nullptr)
	{
		fclose(_stream);
	}
}

} }
